/**
 * Copyright (c) 2005-2009 springside.org.cn
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * 
 * $Id: PropertyFilter.java 873 2010-01-18 16:38:24Z calvinxiu $
 */
package net.uman.framework.modules.orm;

import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;

import net.uman.framework.modules.utils.ReflectionUtils;

import org.apache.commons.lang.StringUtils;
import org.springframework.util.Assert;

/**
 * 与具体ORM实现无关的属性过滤条件封装类.
 * 
 * PropertyFilter主要记录页面中简单的搜索过滤条件,比Hibernate的Criterion要简单.
 * 
 * 使用filter_relation=AND | OR来表示各PropertyFilter之间的关系。缺省是AND
 * 
 * @author calvin
 */
public class PropertyFilter {

	/** 多个属性间OR关系的分隔符. */
	public static final String OR_SEPARATOR = "_OR_";

	public enum RelationType {
		AND, OR;
	}
	
	/** 属性比较类型. */
	public enum MatchType {
		EQ, NEQ, LIKE, LT, GT, LE, GE, IN, NIN;
	}

	/** 属性数据类型. */
	public enum PropertyType {
		S(String.class), I(Integer.class), L(Long.class), N(Double.class), D(Date.class), T(Timestamp.class), B(Boolean.class), F(String.class);

		private Class<?> clazz;

		PropertyType(Class<?> clazz) {
			this.clazz = clazz;
		}

		public Class<?> getValue() {
			return clazz;
		}
	}

	private String[] propertyNames = null;
	private PropertyType propertyType = null;
	private Object propertyValue = null;
	private MatchType matchType = null;

//	public PropertyFilter() {
//	}

	/**
	 * SpringSide封装的PropertyFilter类，可在页面中简单定义filter_EQS_name, filter_LIKES_NAME_OR_LOGIN_NAME的请求参数，可通过HibernateWebUtils的
	 *  buildPropertyFilter(ServletRequest)函数快速构造出PropertiyFilter列表并传递到 HibernateDAO的search(List<PropertyFilter>)函数中，整个过程非
	 *  常自动化，无需太多的特定编 码，filter的命名规则也一目了然。
	 *
 	 * 其中比较类型可选 EQ, NEQ, LIKE, LT, GT, LE, GE, IN, NIN;
	 *
 	 * 比较值类型可选S(String.class), I(Integer.class), L(Long.class), N(Double.class), D(Date.class), T(Timestamp.class), B(Boolean.class), F(String.class);
     *
     * F表示属性名
     * 
 	 * 如果要同时比较多个属性，属性名之间用_OR_分隔.
 	 * 
 	 * 如果值是_ALL_, 意思就是“全部”。这个条件将被忽略
	 *
	 * For Date, the string should be "2010-10-28" 
	 * 
	 * For IN and NIN, the string should be "1,2,4"
	 * 
	 * @param filterName 比较属性字符串,含待比较的比较类型、属性值类型及属性列表. 命名规则为Filter属性前缀_比较类型属性类型_属性名 
	 *                   eg. LIKES_NAME_OR_LOGIN_NAME
	 * @param value 待比较的值.
	 */
	public PropertyFilter(final String filterName, final String... values) {
		String matchTypeStr = StringUtils.substringBetween(filterName, "_","_");
		String matchTypeCode = StringUtils.substring(matchTypeStr, 0, matchTypeStr.length() - 1);
		String propertyTypeCode = StringUtils.substring(matchTypeStr, matchTypeStr.length() - 1, matchTypeStr.length());
		try {
			matchType = Enum.valueOf(MatchType.class, matchTypeCode);
		} catch (RuntimeException e) {
			throw new IllegalArgumentException("filter名称" + filterName + "没有按规则编写,无法得到属性比较类型.", e);
		}

		try {
			propertyType = Enum.valueOf(PropertyType.class, propertyTypeCode);
		} catch (RuntimeException e) {
			throw new IllegalArgumentException("filter名称" + filterName + "没有按规则编写,无法得到属性值类型.", e);
		}

		String propertyNameStr = StringUtils.substringAfterLast(filterName, "_");
		propertyNames = StringUtils.splitByWholeSeparator(propertyNameStr, PropertyFilter.OR_SEPARATOR);

		Assert.isTrue(propertyNames.length > 0, "filter名称" + filterName + "没有按规则编写,无法得到属性名称.");
		if (propertyType == PropertyType.F) {
			// field name
			this.propertyValue = values[0];
		} else {
			//按entity property中的类型将字符串转化为实际类型.
			if (values.length == 1) {
				this.propertyValue = ReflectionUtils.convertStringToObject(values[0], propertyType.getValue());
				if (matchType == MatchType.LE && this.propertyValue instanceof Timestamp) {
					adjustDate((Date)this.propertyValue);
				}
			} else {
				List vs = new ArrayList();
				for (String s : values) {
					Object value = ReflectionUtils.convertStringToObject(s, propertyType.getValue());
					if (matchType == MatchType.LE && value instanceof Timestamp) {
						adjustDate((Date)value);
					}
					vs.add(value);
				}
				this.propertyValue = vs;
			}
		}
	}

	// in case of LET, we should add one day
	private void adjustDate(Date date) {
		GregorianCalendar cal = new GregorianCalendar();
		cal.setTime(date);
		cal.add(Calendar.DAY_OF_MONTH, 1);
		cal.set(Calendar.HOUR_OF_DAY, 0);
		cal.set(Calendar.MINUTE, 0);
		cal.set(Calendar.SECOND, 0);
		date.setTime(cal.getTimeInMillis());
	}
	
	/**
	 * 是否比较多个属性.
	 */
	public boolean isMultiProperty() {
		return (propertyNames.length > 1);
	}

	/**
	 * 获取比较属性名称列表.
	 */
	public String[] getPropertyNames() {
		return propertyNames;
	}

	/**
	 * 获取唯一的比较属性名称.
	 */
	public String getPropertyName() {
		if (propertyNames.length > 1) {
			throw new IllegalArgumentException("There are not only one property");
		}
		return propertyNames[0];
	}

	/**
	 * 获取比较值.
	 */
	public Object getPropertyValue() {
		return propertyValue;
	}

	/**
	 * 获取比较值的类型.
	 */
	public PropertyType getPropertyType() {
		return propertyType;
	}

	/**
	 * 获取比较方式类型.
	 */
	public MatchType getMatchType() {
		return matchType;
	}
}
